package com.g001.model;

import java.util.HashMap;
import java.util.Random;
import java.util.UUID;

import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.particles.ConfigurableEmitter;
import org.newdawn.slick.particles.ParticleSystem;
import org.newdawn.slick.state.StateBasedGame;

import com.g001.enums.BlockType;
import com.g001.enums.MovingBlockType;
import com.g001.enums.ProxBlockType;

public class GameBlock extends IBlock {
	
	private static final Random random = new Random();
	private long timer;
	
	private Image tile;
	private float scale;
	private boolean selected;
	private boolean pulseDirection;
	private boolean shock;
	
	
	private float speed = BlockType.INIT_SPEED;
	private final static float GRAVITY_G = 1.05f;
	
	public float yDestination;
	public float xDestination;
	private boolean falling;
	private boolean proxyUpdated;
	
	private float xRender;
	private float yRender;
	private boolean switching;
	
	private boolean busy;
	
	/** The original emitter we've duplicated */
	private ConfigurableEmitter explosionEmitter;
	private ParticleSystem particleSystem;
	
	public GameBlock(final BlockType blockType, final int i, final int j, final Vector2f gamePosition, ParticleSystem particleSystem) throws SlickException {		
		this.movingBlockType = MovingBlockType.X_Y; // no mvt
		this.tile = blockType.getImage();
		this.shape = new Polygon(BlockType.tab);
		this.blockType = blockType;
		this.gamePosition = gamePosition;
		this.selected = false;
		this.shock = false;
		this.scale = 1f;
		this.i = i;
		this.j = j;
		initPosition();
		setProxiBlocks(new HashMap<ProxBlockType,IBlock>());
		// particules
		this.particleSystem = particleSystem;		
		this.id = UUID.randomUUID();
	}

	@Override
	public void update(GameContainer container, StateBasedGame game, int delta)
			throws SlickException {

			float currentSpeed = 0;
			// to allow a correct switch on the screen (top blocks are not moving also)
			if(getProxyBlock(ProxBlockType.S)!=null && !getProxyBlock(ProxBlockType.S).isSwitching()) {
				xDestination = getProxyBlock(ProxBlockType.S).x;
				yDestination = getProxyBlock(ProxBlockType.S).y-BlockType.HEIGHT;
			}
		 
			movingBlockType = MovingBlockType.getMovingBlockType(x, y, xDestination, yDestination);
			falling = movingBlockType != MovingBlockType.X_Y && !switching;
			
			if(selected){
				pulse(delta);
			}
			if(remove){
				explose(delta);
			}
			if(destroy){
				if(explosionEmitter!=null && particleSystem!=null) {
					explosionEmitter.wrapUp();
					explosionEmitter.reset();
					explosionEmitter.resetState();
				//	System.out.println("REMOVE emitter for "+this+" :"+explosionEmitter.name+" count:"+particleSystem.getEmitterCount());
					particleSystem.removeEmitter(explosionEmitter);
					explosionEmitter = null;
				}
			}
		
			if(shock) {
				chock(delta);
			} 
			if(!shock)
			{
				xRender = x;
				yRender = y;
			}
			// we do not check falling if it is shaked
			if(falling){
				// remove all connextion to blocks around
				IBlock N = getProxyBlock(ProxBlockType.N);
				IBlock S = getProxyBlock(ProxBlockType.S);
				clearAllProxyBlocks();
				setProxyBlock(ProxBlockType.N, N);
				setProxyBlock(ProxBlockType.S, S);
				// and ask to refresh the proxy
				proxyUpdated = false;
				speed = speed * GRAVITY_G  ;
				currentSpeed = speed * delta;
				matchesChecked = false;
			} else {
				currentSpeed = BlockType.INIT_SPEED * delta;
				if(!proxyUpdated) {
					updateProxy();
				}
			}
			
			
			switch(movingBlockType){
				case XP_YP : 
					x = x + currentSpeed; 
					if(x>xDestination) {
						x = xDestination;
						movingBlockType = MovingBlockType.X_YP;
					}
					y = y + currentSpeed; 
					if(y>yDestination) {
						y = yDestination;
						if(movingBlockType == MovingBlockType.X_YP) {
							movingBlockType = MovingBlockType.X_Y;
						} else {
							movingBlockType = MovingBlockType.Y_XP;
						}
					}
					break;
				case XP_YN :
					x = x + currentSpeed; 
					if(x>xDestination) {
						x = xDestination;
						movingBlockType = MovingBlockType.X_YN;
					}
					y = y - currentSpeed; 
					if(y<yDestination) {
						y = yDestination;
						if(movingBlockType == MovingBlockType.X_YN) {
							movingBlockType = MovingBlockType.X_Y;
						} else {
							movingBlockType = MovingBlockType.Y_XP;
						}
					}
					break;
				case XN_YP : 
					x = x - currentSpeed; 
					if(x<xDestination) {
						x = xDestination;
						movingBlockType = MovingBlockType.X_YP;
					}
					y = y + currentSpeed; 
					if(y>yDestination) {
						y = yDestination;
						if(movingBlockType == MovingBlockType.X_YP) {
							movingBlockType = MovingBlockType.X_Y;
						} else {
							movingBlockType = MovingBlockType.Y_XN;
						}
					}
				    break;
				case XN_YN :
					x = x - currentSpeed; 
					if(x<xDestination) {
						x = xDestination;
						movingBlockType = MovingBlockType.X_YN;
					}
					y = y - currentSpeed; 
					if(y<yDestination) {
						y = yDestination;
						if(movingBlockType == MovingBlockType.X_YN) {
							movingBlockType = MovingBlockType.X_Y;
						} else {
							movingBlockType = MovingBlockType.Y_XN;
						}
					}
					break;
				case X_YP  : 
					y = y + currentSpeed; 
					if(y>yDestination) {
						y = yDestination;
						movingBlockType = MovingBlockType.X_Y;
					}
					break;
				case X_YN  : 
					y = y - currentSpeed; 
					if(y<yDestination) {
						y = yDestination;
						movingBlockType = MovingBlockType.X_Y;
					}
					break;
				case Y_XP  : 
					x = x + currentSpeed;
					if(x>xDestination) {
						x = xDestination;
						movingBlockType = MovingBlockType.X_Y;
					}
					break;
				case Y_XN  : 
					x = x - currentSpeed; 
					if(x<xDestination) {
						x = xDestination;
						movingBlockType = MovingBlockType.X_Y;
					}
					break;
				case X_Y : 
					speed = BlockType.INIT_SPEED;
					switching = false;
					busy = false;
					break;
				default : throw new IllegalArgumentException("incorrect update! movingBlockType = "+movingBlockType);
			}
			updateShape();
		
	}


	@Override
	public void render(GameContainer container, StateBasedGame game, Graphics g)
			throws SlickException {
		
		g.drawImage(tile.getScaledCopy(scale), xRender + ((1-scale) * BlockType.WIDTH)/2, yRender + ((1-scale) * BlockType.HEIGHT)/2);
//		g.setColor(Color.white);
//		g.drawString(i+","+j, x+15, y+20);
//		    if(falling){
//		    	g.drawString("!", x+8, y+10);
//		    }
//		    if(!proxyUpdated){
//		    	g.drawString("#", x+12, y+10);
//		    }
//		    
//		g.draw(shape);
	}

	@Override
	protected void initPosition() {
		// at the init only - then based on the BORDERBLOCK class!!
		if(i%2==1) {
			if(selected) {
				 x = i * BlockType.WIDTH  + gamePosition.x; 
				 y = j * BlockType.HEIGHT + gamePosition.y + BlockType.HEIGHT/2; 
			} else {
				 x = i * BlockType.WIDTH  + gamePosition.x; 
				 y = j * BlockType.HEIGHT + gamePosition.y + BlockType.HEIGHT/2;
			}
		} else {
			if(selected) {
				x = i * BlockType.WIDTH  + gamePosition.x ; 
				y = j * BlockType.HEIGHT + gamePosition.y ; 
			} else {
				x = i * BlockType.WIDTH  + gamePosition.x ; 
				y = j * BlockType.HEIGHT + gamePosition.y;
			}
		}
		x = x - i * 20;
		updateShape();
	}
	
	
	public void pulse(int delta){
		busy = true;
		if(pulseDirection) {
			// decrease
			if(scale<BlockType.PULSE_MIN) {
				pulseDirection = !pulseDirection;
			} else {
				scale -= BlockType.PULSE_INTERVAL * delta;
			}
		} else {
			// increase
			if(scale>BlockType.PULSE_MAX) {
				pulseDirection = !pulseDirection;
			} else {
				scale += BlockType.PULSE_INTERVAL * delta;
			}
		}
	}
	
	
	public void explose(int delta) {
		busy = true;
		if(explosionEmitter!=null) explosionEmitter.setPosition(x+BlockType.WIDTH/2,y+BlockType.HEIGHT/2);
		pulse(delta);		
		if(timer<System.currentTimeMillis()) {
			if(scale>0.01f) {
				scale -= BlockType.EXPLOSE_MIN * delta;
				if(explosionEmitter!=null) explosionEmitter.wrapUp();
			} else {
				destroy = true;
				
			}
		}
	}
	
	private void chock(int delta) {
		scale = 1;
		busy = true;
		if(System.currentTimeMillis()<=timer) {
		xRender = xDestination - (random.nextBoolean()?- random.nextInt(BlockType.SHOCK_ON_X):random.nextInt(BlockType.SHOCK_ON_X));
		yRender = yDestination - (random.nextBoolean()?- random.nextInt(BlockType.SHOCK_ON_Y):random.nextInt(BlockType.SHOCK_ON_Y));
		} else {
			// reset shock
			xRender = xDestination;
			yRender = yDestination;
			shock = false;
		}
	}

	@Override
	public void setSelected(boolean b) {
		scale = 1;
		selected = b;
	}
	@Override
	public boolean isSelected() {
		return selected;
	}
	
	public String toString(){
		return  "["+i+","+j+"] "+String.valueOf(blockType)+" "+getId();
	}

	@Override
	public void setShock() {
		timer = System.currentTimeMillis() + BlockType.SHOCK_DURATION;
		xDestination = x;
		yDestination = y;
		this.shock = true;
	}
	
	@Override
	public void moveTo(final IBlock b){
		busy = true;
		xDestination = b.x;
		yDestination = b.y;
		switching = true;
		setProxyUpdated(false);
		matchesChecked = false;
	}

	@Override
	public boolean isSwitching() {
		return switching;
	}

	@Override
	public void setSwitching(boolean switching) {
		this.switching = switching;
	}

	@Override
	public void moveBack(IBlock b) {
		busy = true;
		xDestination = b.x;
		yDestination = b.y;
		switching = true;
		setProxyUpdated(false);
		matchesChecked = false;
	}
	
	@Override
	public boolean isProxyUpdated() {
		return proxyUpdated;
	}

	@Override
	public void setProxyUpdated(boolean proxyUpdated) {
		this.proxyUpdated = proxyUpdated;
	}

	@Override
	public void setRemove(boolean b) {
		remove = b;
		if(remove) {
			matchesChecked = true;
			if(explosionEmitter==null && particleSystem!=null) {
				// should be added once
				ConfigurableEmitter explosionEmitterOri = (ConfigurableEmitter) particleSystem.getEmitter(0);
				explosionEmitterOri.setPosition(x,y);
				explosionEmitter = explosionEmitterOri.duplicate();
				explosionEmitterOri.setPosition(x,y);
				explosionEmitter.name = "em_"+System.currentTimeMillis();
				particleSystem.addEmitter(explosionEmitter);
				//System.out.println("ADD emitter for "+this+" :"+explosionEmitter.name+" count:"+particleSystem.getEmitterCount());
				explosionEmitter.setPosition(x+BlockType.WIDTH/2,y+BlockType.HEIGHT/2);
			}
			timer = System.currentTimeMillis() + BlockType.DESTROYED_DURATION/6;
		}
	}

	@Override
	public boolean isFalling() {
		return falling;
	}

	@Override
	public boolean isBusy() {
		return busy;
	}

}
